#define STRICT
#define WIN32_LEAN_AND_MEAN

#include "stdafx.h"
#include <windows.h>
#include <time.h>
#include "Scene.h"
#include "..\Render\Render.h"
#include "..\GUI\GUI.h"
#include "Viewer.h"
#include "Model.h"
#include "..\Window.h"

#include <vector>
#include "..\..\Global\globaldef.h"


void CViewer::OnPacketNetwork(Connection::Packet* pPacket) {

	if(!pPacket)
		return;

	const int packetID = pPacket->header.packetID;
	if(packetID == NETWORK_PROBE) {
		//Handled by network library
	}
	
	if(packetID == NETWORK_DISCONNECT) {
		//Handled by network library
	}
}

#pragma message("Todo; refactor")
extern bool CharBuffer_Copy(tCharBuffer* pDest, tCharBuffer* pSource);

void CViewer::OnPacketQuery(Connection::Packet* pPacket) {

	if(!pPacket)
		return;

	Model *pSM = NULL;
	QuerysStructs::tViewerInfoQuery* pData = (QuerysStructs::tViewerInfoQuery*)pPacket->pData;
	const bool targetExists = Scene::GetGeometry(&pSM, &pData->target);
	const int packetID = pPacket->header.packetID;
	uint sendSize = 0;

	switch(packetID) {

		case QUERY_RESPONCE_OBJECTEXISTS: {

			/*
			Get/fill responce header
			*/
			QuerysStructs::tQueryHeader queryHeader;
			queryHeader.isPresent = targetExists;
			tCharBuffer targetID;
			if(pSM)
				pSM->GetID(&targetID);
			else
				CharBuffer_Copy(&targetID, &pData->target);

			memcpy(&queryHeader.targetName, &targetID, sizeof(tCharBuffer));
			sendSize = sizeof(QuerysStructs::tQueryHeader);

			/*
			Send data off
			*/
			if(!CViewer::g_server.SendDataPacket((LPVOID)&queryHeader, sendSize, QUERY_RESPONCE_OBJECTEXISTS, 0))
				throw tException("Failed to send data");

		} break;

		case QUERY_RESPONCE_SYNCHRONIZED: {

			byte* pResponceData = NULL;
			QuerysStructs::tGeometryPartHeader *pGeometryPartsHeader = NULL;
			QuerysStructs::tInstanceChildHeader* pInstancingChildrenHeader = NULL;

			try {

				/*
				Send a query responce full with data if the target exists
				*/
				if(targetExists) {

					/*
					Get/fill responce header
					*/
					QuerysStructs::tQueryHeader queryHeader;
					queryHeader.isPresent = true;
					tCharBuffer targetID;
					pSM->GetID(&targetID);
					memcpy(&queryHeader.targetName, &targetID, sizeof(tCharBuffer));
					sendSize += sizeof(QuerysStructs::tQueryHeader);

					/*
					Get geometry information
					*/
					QuerysStructs::tGeometryHeader geometryHeader;
					if(!pSM->GetInfoGeometry(&geometryHeader, &pGeometryPartsHeader, NULL))
						throw tException("Failed to get geometry information");
					sendSize += sizeof(QuerysStructs::tGeometryHeader);

					uint partCount = geometryHeader.partCount;
					uint vertexSize = GetVertexFormatSize(geometryHeader.vertexFormat);
					for(uint i = 0; i < partCount; i++)
						sendSize += sizeof(QuerysStructs::tGeometryPartHeader); /*(sizeof(QuerysStructs::tGeometryPartHeader) + (pGeometryPartsHeader[i].triangeCount * vertexSize * 3));*/

					/*
					Get instancing information
					*/
					QuerysStructs::tInstancingHeader instancingHeader;
					if(!pSM->GetInfoInstancing(&instancingHeader, pInstancingChildrenHeader))
						throw tException("Failed to get instancing information");
					sendSize += sizeof(QuerysStructs::tInstancingHeader);

					uint childInstanceCount = instancingHeader.childCount;
					sendSize += (sizeof(QuerysStructs::tInstanceChildHeader) * childInstanceCount);

					/*
					Get deformation information
					*/
					QuerysStructs::tDeformationHeader deformationHeader;
					if(!pSM->GetInfoDeformation(&deformationHeader))
						throw tException("Failed to get deformation information");
					sendSize += sizeof(QuerysStructs::tDeformationHeader);

					/*
					Package data up
					*/
					pResponceData = new byte [sendSize];
					byte* pWriter = pResponceData;

					memcpy(pWriter, &queryHeader, sizeof(QuerysStructs::tQueryHeader));
					pWriter += sizeof(QuerysStructs::tQueryHeader);

					memcpy(pWriter, &geometryHeader, sizeof(QuerysStructs::tGeometryHeader));
					pWriter += sizeof(QuerysStructs::tGeometryHeader);

					for(uint i = 0; i < partCount; i++) {

						memcpy(pWriter, &pGeometryPartsHeader[i], sizeof(QuerysStructs::tGeometryPartHeader));
						pWriter += sizeof(QuerysStructs::tGeometryPartHeader);
					}

					memcpy(pWriter, &instancingHeader, sizeof(QuerysStructs::tInstancingHeader));
					pWriter += sizeof(QuerysStructs::tInstancingHeader);
					
					for(uint i = 0; i < childInstanceCount; i++) {

						memcpy(pWriter, &pInstancingChildrenHeader[i], sizeof(QuerysStructs::tInstanceChildHeader));
						pWriter += sizeof(QuerysStructs::tInstanceChildHeader);
					}

					memcpy(pWriter, &deformationHeader, sizeof(QuerysStructs::tDeformationHeader));
					pWriter += sizeof(QuerysStructs::tDeformationHeader);
					
				}
				/*
				Send a query responce with no aditional data if the target dosen't exists
				*/
				else {
				
					/*
					Get/fill responce header
					*/
					QuerysStructs::tQueryHeader queryHeader;
					queryHeader.isPresent = false;
					tCharBuffer targetID;
					memset(&queryHeader.targetName, 0, sizeof(tCharBuffer));
					sendSize += sizeof(QuerysStructs::tQueryHeader);

					pResponceData = new byte [sendSize];
					byte* pWriter = pResponceData;

					memcpy(pWriter, &queryHeader, sizeof(QuerysStructs::tQueryHeader));
					pWriter += sizeof(QuerysStructs::tQueryHeader);
				}

				/*
				Send data off
				*/
				if(!CViewer::g_server.SendDataPacket((LPVOID)pResponceData, sendSize, QUERY_RESPONCE_SYNCHRONIZED, 0))
					throw tException("Failed to send data");

				if(pResponceData)
					delete [] pResponceData;

				if(pInstancingChildrenHeader)
					delete [] pInstancingChildrenHeader;

				if(pGeometryPartsHeader)
					delete [] pGeometryPartsHeader;
			}
			catch(tException excpetion) {

				if(pResponceData)
					delete [] pResponceData;

				if(pInstancingChildrenHeader)
					delete [] pInstancingChildrenHeader;

				if(pGeometryPartsHeader)
					delete [] pGeometryPartsHeader;

				byte* pResponceData = NULL;
				QuerysStructs::tGeometryPartHeader *pGeometryPartsHeader = NULL;
				byte** pGeometryPartData = NULL;
				uint sendSize = 0;
			}
		}break;
	}
}

//	if(packetID == QUERY_RESPONCE_GEOMETRY) {
//
////		Beep(50,100);
//		QuerysStructs::tGeometryHeader responce = 
//		{ 
//			targetExists ? pSM->m_TriangleCount : 0 
//		};
//		CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_GEOMETRY, 0);
//	}
//	
//	/*if(packetID == QUERY_RESPONCE_INSTAINCING) {
//
//		const int childInstanceCount = targetExists ? pSM->ChildInstancingCount() : 0;
//
//		QuerysStructs::tViewerInstanceInfoResponce responce = 
//		{
//			targetExists, 
//			childInstanceCount, 
//			tCharBuffer(), 
//			tProceduralData()
//		};
//
//		if(childInstanceCount == 0) {
//		
//			CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_INSTAINCING, 0);
//		}
//		else {
//
//			for(uint i = 0; i < childInstanceCount; i++) {
//
//				Model* pMesh = NULL;
//				if(pSM->GetChildInstance(i, &pMesh, &responce.childProcedrual)) {
//					responce.childMesh = pMesh->m_ID;
//					CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_INSTAINCING, 0);
//				}
//			}
//		}
//	}*/
//
//	if(packetID == QUERY_RESPONCE_TEXTURES) {
//
////	Beep(50,50);
//		/*
//		QuerysStructs::tViewerTextureInfoResponce responce =
//		{
//			{{'\0'}, {'\0'}, {'\0'}}
//		};
//
//		if(targetExists) {
//			
//			for(u_int i = 0; i < Model::TEXTURE_COUNT; i++) {
//
//				if(pSM->m_Textures[i]) 
//					memcpy(&responce.textureFilenames[i], pSM->m_Textures[i]->filename.c_str(), 512);
//			}
//		}
//		CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_TEXTURES, 0);*/
//	}
//
//
//	if(packetID == QUERY_RESPONCE_DEFORMATION) {
//
//		QuerysStructs::tViewerDeformationInfoResponce responce =
//		{
//			0,0,0,
//		};
//
//		if(targetExists) {
//			
//			responce.remove = pSM->m_deformation;
////			responce.proceduralFormat = pSM->m_deformation.proceduralFormat;
//			responce.scale = pSM->m_deformationScale;
//		}
//		CViewer::g_server.SendDataPacket((LPVOID)&responce, sizeof(responce), QUERY_RESPONCE_DEFORMATION, 0);
//	}
//
//	if(packetID == QUERY_RESPONCE_PARAMETERS) {
//
//		QuerysStructs::tViewerParameterInfoResponce responce =
//		{
//			{0}, 0, 0, 0,
//		};
//
//		if(targetExists) {
//			
//			if(pSM->m_Shader) {
//				pSM->m_Shader->filename.copy(responce.shaderFilename, MAX_TEXTURE_BYTES);
//				responce.shaderFeatureSet = pSM->m_Shader->Attributes;
//				responce.inputFeatureSet = pSM->m_Shader->VertexFormat;
//			}
//
//
//			
//		}
//	}
//}
 

bool GetTargetMesh(tCharBuffer* pTargetName, Model** pOut) {

	if(!pOut || !pTargetName)
		return false;

	Model* pTargetMesh = NULL;
	if(!Scene::GetGeometry(&pTargetMesh, pTargetName)) {

		pTargetMesh = new Model(pTargetName);
		if(!pTargetMesh)
			return false;

		if(!Scene::InsertGeometry(pTargetMesh)) {
			pTargetMesh->Release();
			delete pTargetMesh;
		}
	}
		
	*pOut = pTargetMesh;
	return true;
}

bool GetTargetLight(tCharBuffer* pTargetName, tLight** pOut) {

	if(!pOut || !pTargetName)
		return false;

	tLight* pTargetLight = NULL;
	if(!Scene::GetLight(&pTargetLight, pTargetName))
		return false;
		
	*pOut = pTargetLight;
	return true;
}

void CViewer::OnPacketUpdate(Connection::Packet* pPacket) {
	
	if(!pPacket)
		return;

	const int packetID = pPacket->header.packetID;
	tCharBuffer* pTargetName = (tCharBuffer*)pPacket->pData;
	Model* pTargetMesh = NULL;
	tLight* pTargetLight = NULL;

	switch(packetID) {
	
		case UPDATE_TRANSFORM:
		{
			tMatrix4* pTransform = (tMatrix4*)(pPacket->pData + sizeof(tCharBuffer));

			/*put first, else mem leak*/
			if(GetTargetLight(pTargetName, &pTargetLight))
				pTargetLight->UpdateTransform(pTransform);
			else
			if(GetTargetMesh(pTargetName, &pTargetMesh))
				pTargetMesh->UpdateTransform(pTransform);
		}
		break;

		case UPDATE_MESHGEOMETRY:
		{
			HeaderStructs::tGeometryHeader* pHeader = (HeaderStructs::tGeometryHeader*)pPacket->pData;
			byte* pGeometry = (pPacket->pData + sizeof(HeaderStructs::tGeometryHeader));
			if(GetTargetMesh(pTargetName, &pTargetMesh)) {
				
				if(pHeader->remove)
					Scene::RemoveGeometry(pTargetMesh);
				else
					pTargetMesh->UpdateGeometry(pHeader, pGeometry);
			}
		}
		break;

		case UPDATE_MESHPARAMETERS:
		{
			HeaderStructs::tParametersHeader* pHeader = (HeaderStructs::tParametersHeader*)pPacket->pData;
			if(GetTargetMesh(pTargetName, &pTargetMesh))
				pTargetMesh->UpdateParameters(pHeader);
		}
		break;

		case UPDATE_MESHSHADER:
		{
			HeaderStructs::tShaderHeader* pHeader = (HeaderStructs::tShaderHeader*)pPacket->pData;
			char* pData = pPacket->pData + sizeof(HeaderStructs::tShaderHeader);
			if(GetTargetMesh(pTargetName, &pTargetMesh))
				pTargetMesh->UpdateShader(pHeader, pData);

		}
		break;

		case UPDATE_MESHCHANNEL:
		{
			HeaderStructs::tChannelHeader* pHeader = (HeaderStructs::tChannelHeader*)pPacket->pData;
			if(GetTargetMesh(pTargetName, &pTargetMesh))
				pTargetMesh->UpdateChannel(pHeader);
		}
		break;

		case UPDATE_MESHSAMPLER:
		{
			HeaderStructs::tSamplerHeader* pHeader = (HeaderStructs::tSamplerHeader*)pPacket->pData;
			if(GetTargetMesh(pTargetName, &pTargetMesh))
				pTargetMesh->UpdateSampler(pHeader);
		}
		break;

		case UPDATE_SCENESAMPLER:
		{
			HeaderStructs::tSceneSamplerHeader* pHeader = (HeaderStructs::tSceneSamplerHeader*)pPacket->pData;
			char* pData = (char*)pHeader + sizeof(HeaderStructs::tSceneSamplerHeader);
			HMATERIAL material = NULL;
			if(Materials::GetMaterial(std::string(pHeader->target.Buffer), &material)) {
			
				if(pHeader->remove)
					Materials::Release(std::string(pHeader->target.Buffer));
				else
					Materials::UpdateMaterial(pHeader->type, pData, material);

//				Materials::Release(material);	//Free handler reference
			}
		}
		break;

		//case UPDATE_MESHINSTANCING:
		//{
		//	HeaderStructs::tInstanceHeader* pHeader = (HeaderStructs::tInstanceHeader*)pPacket->pData;
		//	byte* pParameters = (pPacket->pData + sizeof(HeaderStructs::tInstanceHeader));
		//	if(GetTargetMesh(pTargetName, &pTargetMesh))
		//		pTargetMesh->UpdateInstancing(pHeader, pParameters);
		//}
		//break;

		case UPDATE_INSTANCESAMPLER:
		{
			HeaderStructs::tInstanceChildSampler* pHeader = (HeaderStructs::tInstanceChildSampler*)pPacket->pData;
			if(GetTargetMesh(pTargetName, &pTargetMesh))
				pTargetMesh->UpdateInstanceChild_Sampler(pHeader);
		}
		break;

		case UPDATE_INSTANCEMASK:
		{
			HeaderStructs::tMaskHeader* pHeader = (HeaderStructs::tMaskHeader*)pPacket->pData;
			char* pData = (char*)pHeader + sizeof(HeaderStructs::tMaskHeader);

			if(GetTargetMesh(pTargetName, &pTargetMesh))
				pTargetMesh->UpdateInstanceChild_Mask(pHeader, pData);
		}
		break;

		case UPDATE_INSTANCEUV:
		{
			HeaderStructs::tInstanceChildUV* pHeader = (HeaderStructs::tInstanceChildUV*)pPacket->pData;
			if(GetTargetMesh(pTargetName, &pTargetMesh))
				pTargetMesh->UpdateInstanceChild_UV(pHeader);
		}
		break;

		case UPDATE_INSTANCECHANGE:
			{

			HeaderStructs::tInstanceChildChange* pHeader = (HeaderStructs::tInstanceChildChange*)pPacket->pData;
			byte* pParameters = (pPacket->pData + sizeof(HeaderStructs::tInstanceSamplerHeader));
			if(GetTargetMesh(pTargetName, &pTargetMesh))
				pTargetMesh->UpdateInstanceChild(pHeader);
			}
			break;

	/*	case UPDATE_DEFORMATION:
		{
			HeaderStructs::tDeformationHeader* pHeader = (HeaderStructs::tDeformationHeader*)pPacket->pData;
			if(GetTargetMesh(pTargetName, &pTargetMesh))
				pTargetMesh->UpdateDeformation(pHeader);
		}
		break;*/

		case UPDATE_SCENELIGHTS:
		{

			HeaderStructs::tSceneLight* pHeader = (HeaderStructs::tSceneLight*)pPacket->pData;
			tLight* pLight = NULL;

			if(pHeader->remove) {

				if(Scene::GetLight(&pLight, pTargetName) == false)
					Scene::RemoveLight(pLight);
			}
			else {

				if(Scene::GetLight(&pLight, pTargetName) == false) {

					if(pHeader) {

						switch(pHeader->type) {

							case 0:
								pLight = new tLight();
								break;
							case 1:
								pLight = new tDirectLight();
								break;
							case 2:
								GUI::PrintWarning("Implement spotlight source type");
								break;
						}
					
						if(pLight)
							CharBuffer_Copy(&pLight->m_Name, &pHeader->target);
					}

					Scene::InsertLight(pLight);
				}

				if(pLight) {

					pLight->m_DiffuseScale = pHeader->diffuse;
					pLight->m_SpecularScale = pHeader->specular;
					pLight->m_Brightness = pHeader->brightness;
					pLight->m_Diffuse = D3DXVECTOR3((float*)&pHeader->color);
				}
			}
		
		}
		break;

		}
}

void CViewer::PacketHandler(Connection::Packet* pPacket) {

	if(!pPacket)
		return;

	const int packetID = pPacket->header.packetID;
	if(packetID & PACKET_NETWORK) {
		OnPacketNetwork(pPacket);
	}
	
	if(packetID & PACKET_QUERY) {
		OnPacketQuery(pPacket);
	}
	
	if(packetID & PACKET_UPDATE) {
		OnPacketUpdate(pPacket);
	}

	pPacket->ReleaseHeapData();
}

